{*****************************************************************************

Title: SIPS Vibrato Script

Author: R.D.Villwock aka 'Big Bob'

First Written: Feb 20, 2006

Current Version: 1.51

Last Modified: June 5, 2007 



  This script is part of a 'Suite' of scripts for performance effects known as

the 'Solo Instrument Performance Suite', SIPS.  Currently, there are two members

in the suite, the SIPS Legato Script and the SIPS Vibrato Script, SLS and SVS.

This version of the SVS depends on the Solo-Mode Logic for the suite and thus

the SVS cannot be run effectively by itself. Therefore, if the SLS installed in
front of the SVS, or if the SLS is disabled or bypassed, the SVS will disable
itself automatically.

*******************************************************************************

NOTE: To compile this source code, you will need to use V1.22.4 or higher of

Nils Liberg's KScript Editor. Since this modularized version of SIPS uses the

'import' feature of the NL Editor, you will also need the source text files

for all the 'External Modules' (in the same folder) to compile this script.

*******************************************************************************

External SIPS Modules Needed:

  SIPSXM-GSR     General Support Routines

  SIPSXM-SVS     Common SIPS Routines and Data types, Customized for the SVS

  SIPSXM-Ren     Pulls in ui_control callbacks for the Rename-Panel & Btn-Menus

  SIPSXM-UPS     User Preset Support for SIPS (including Rename & Error Panels)

Other External Modules Needed:

  ISCS_V101      Interscript Communication System 
-------------------------------------------------------------------------------}  
  

import "SIPSXM-GSR V151.txt"

import "SIPSXM-SVS V151.txt" 

import "SIPSXM-UPS V151.txt"
import "ISCS_V101.txt"    { Interscript Communication System Module } 

 
on init
{  make_perfview }
  {------------------------- ISCS Interface Block ---------------------------}

                 {------ SIPS SVS Code Assignments ------}

  declare const SVS_ID := 1002   { Registered SID for the SVS Script }
                          { Omni Messages }

                         { Client Messages }

  declare const import_block := 1   { Import Preset if sender is SVS }  

                      { Local Call Selectors }
  declare const FetchPreset := 1    { Trigger Recall_Preset }               
              {----------- SLS Code Assignments -----------}
  declare const SLS_ID := 1001   { Registered SID for the SLS Script }                                  
                           { Omni Messages }

  declare const starting_phrase := 1    { Starting first note after silence }

  declare const ending_phrase := 2      { Ending last note of phrase }

  declare const ending_prior_note := 3  { Ending prior fade_out note }

  declare const starting_xfade := 4     { Starting a legato transition }

  declare const ending_xfade := 5       { Ending a legato transition }

  declare const new_inst_range := 6     { Instrument Range:  EP[0], EP[1] = low, top }
                      

              {----------- VXF Code Assignments -----------}

  declare const VXF_ID := 1500   { Registered SID for the VXF }

                          { Omni Messages }
                          

                     { Client Messages/Replies }

  declare const get_nlayers := 1 { Return current nlayers in EP[0] }

    declare const vxfFULL := 2   { VXF at max polyphony, discard new note }

  

  on_init_ISCS(SVS_ID)          { Generate ISCS data declarations }  

  {--------------------- End of ISCS Interface Block ------------------------}
     
  { --------- Environment Parameters for the SVS-V105 & V110..V150 ----------}

  declare const SLS  := 1001  { SIPS Member Script Code for the SLS }

  declare const SVS  := 1002  { SIPS Member Script Code for the SVS }

  declare const V105 := 100   { User Preset Format Code for V105 & V1051 }

  declare const V110 := 101   { User Preset Format Code for V110 }
  declare const V150 := 102   { User Preset Format Code for V150 }  

  declare const MSC  := SVS   { Member Script Code for this script }

  declare const PFC  := V150  { User Preset Format Code for this script }
    
  { ----------------- Global Inter-Script Data Array ----------------- }

  on_init_GlobalArray  { Declare and initialize Global Data Array, *GSR* } 

    

  { ------------------------ Preset Limits --------------------------- }

  declare const KeyParms := 16     { #of paramaters for a Built-in Preset }

  declare const MainPresets := 10  { Number of Main (ie Built-In) presets }

  declare const MainSize := MainPresets * KeyParms  { Must not exceed 512 }

  on_init_Set_UserLimits           { Establish User Preset Limits, *UPS* }


                         { Env Menu Indices }

  declare const Amt_Only := 1      { Menu Index, Vib-Amt knob only }
  declare const Env_Only := 2      { Menu Index, Envelope only }
  declare const Amt_and_Env := 3   { Menu Index, Vib-Amt + Envelope }  
                            { Defaults }
  declare const DefAmt := 100     { Default Vibrato Amount }                               
  declare const DefRng := 100     { Default MIDI CC Range = 100% }
  declare const No_Cmd  := -1     { Preset Idle, negative values are used for commands }  
                          { Control Limits }
  declare const MaxRise := 20     { Slowest vibrato rise time in cycles }
  declare const MaxDecay := 100   { Max Vibrato Envelope decay in cycles }
  declare const MaxOnset := 25    { Max Vibrato Onset delay in cycles }
  declare const MaxDepth := 500   { Maximum Tremolo Depth 5.00 db }
  declare const MaxSpdDrift := 30 { Maximum Vibrato speed drift in per cent }
                                  { CAUTION: Must be less than 100 }
  declare const MaxDwell := 90    { Almost a square wave } 
  declare const MaxVar := 30      { Max Width/Depth Variation as decimal percent }
  declare const MaxRng := 200     { Max MIDI CC Range = 200% }
  declare const MaxSpeed := 600   { Fastest Vibrato Speed, 6.00 Hz }

  declare const MaxWidth := 3000  { Maximum Vibrato Width, 30.00 cents }

  declare const MinRng := 10      { Min MIDI CC Range = 10% }

  declare const MinSpeed := 200   { Slowest Vibrato Speed, 2.00 Hz }
                                  { CAUTION: Must be greater than zero }
  declare const TopCC := 119      { Top useable MIDI CC# }                                  
                           { Miscellaneous }
  declare const Full_Env := 1000    { Full or unity Envelope }
  declare const ModWheel := 1       { MIDI Mod Wheel code }

  declare const NoID := 0           { Impossible ID }
  declare const NoParm := -100000   { Used as a token for 'undefined' parameter }  

  declare const false := 0          { Boolean False }

  declare const true := 1           { Boolean True }
  declare const PitchWheel := 128   { K2 CC code for bender wheel }
  declare const Tick := 100         { Staircase step time in usec }


  { -------- Envelope Generator States ---------- }

  declare const Restart := 0

  declare const Onset_Delay := 1

  declare const Ramp_To_Full := 2

  declare const Diminuendo := 3

  { -------------- String Constants --------------- }

  declare @LowCap            { Caption for low key in IR }

  LowCap := ' Low Key: '

  declare @TopCap            { Caption for high key in IR }

  TopCap := ' High Key: '
    
  { ------------------- String Array Constants --------------------- }
  on_init_CC           { Allocate and intialize MIDI CC menu names, *GSR* }

  on_init_CharSet      { Allocate and initialize User Name Character Set, *UPS* }
  on_init_ScaleTones   { Allocate and initialize the Scale Tone array, *GSR* }



  { ---------------------- Preset/Panel System -------------------------- }

  declare PresetBfr[MaxParms]   { Buffer to hold one Preset, Main or User }

  on_init_Set_UIX             { Assign an index to each UI control, *SVS* }
  { ------------------------- Main Presets ------------------------ }  

  declare MainParms[MainSize] := ...    

                         { Presets by BB } ...
    {Inst 0}(  0, 300,400,33, 0, 0, 0, 0, 0, 0, 1, 1,  1,100, 1,100, ...                         
    {Inst 1}  62, 200,400,33,15,15,10, 4,20,10, 5, 5, 35, 55, 3,100, ...
                         { Presets by TK } ...
    {Inst 2}   0, 750,483,32,16, 0, 3,11,11,12, 1, 5, 35, 42, 3,100, ...
    {Inst 3}  10, 992,381,13,12,11,10,23,10, 3, 1, 3, 71, 66, 3,100, ...
                         { Presets by AK } ...
    {Inst 4}  62, 908,460,29,15,15,10, 4,20,10, 4, 3, 35, 55, 3,100, ...
    {Inst 5}  62, 752,400,33,15,15,10, 4,20,10, 1, 2, 35, 55, 3,100, ...
    {Inst 6} 124,1688,400,22, 9, 9,10, 4,20,10, 1, 3, 35, 55, 3,100, ...
                         { Presets by MN } ...
    {Inst 7}  75,1251,425,20,12,15, 7, 4,18,10, 2, 3,  5, 65, 3,100, ...
    {Inst 8}  80,1600,400,37,15,15, 9, 6,15, 8, 2, 3,  4, 70, 3,100, ...                         
    {Inst 9}  80,1000,350,25,15,15, 8, 4,20,10, 2, 3,  5, 65, 3,100)
  { -------------------------- User Presets -------------------------- }

  on_init_UserPresets  { Build default or restore saved User Presets, *UPS* }
  
  { ------------ Main Control Panel, Column 1 --------------- }
  declare ui_label DepthCap (1,1) { Depth Caption }

    set_text(DepthCap,'  Volume Swing') 

  declare ui_menu Preset   { Instrument Preset Menu Button }

    add_menu_item (Preset,' -- Presets --',No_Cmd)  { Menu Hdr }
    Preset := No_Cmd   { Default Preset to display Menu Header }

    { ------------------- Main Presets ------------------------ }
    add_menu_item (Preset,'* Basic Setup *',           0) { Default Inst }
    add_menu_item (Preset,'Bassoon               AK7', 4)  
    add_menu_item (Preset,'Clarinet 1              BB',1) 
    add_menu_item (Preset,'Clarinet 2             AK8',5)  
    add_menu_item (Preset,'Soft Flute            TK12',2)
    add_menu_item (Preset,'French Horn          MN3',  9)
    add_menu_item (Preset,'Trombone 1           TK13', 3)
    add_menu_item (Preset,'Trombone 2           MN2',  8)  
    add_menu_item (Preset,'Trumpet 1             MN1', 7)
    add_menu_item (Preset,'Trumpet 2            AK9',  6)      
  { -------------------- User Presets ----------------------- }

  on_init_Add_UserMenu { Add User Presets and Commands to Menu, *UPS* }

  { ------------------ End Preset Menu ---------------------- }

  on_init_Depth { Declare Depth Knob and associated MIDI controls, *SVS* }

  { ------------ Main Control Panel, Column 2 --------------- }

  declare ui_label WidthCap (1,1) { Width Caption }

    set_text(WidthCap,'Pitch Swing (cts)')

  declare ui_knob Width (0,MaxWidth,100) { Display in cents }
    set_text(Width,' Width')
    Width := MainParms[uWidth] { Default Instrument Width }

  declare ui_label WaveCap (1,1)  { Waveform Caption }

    set_text(WaveCap,'Waveform Shape')    

  declare ui_knob PkDwell (0,MaxDwell,100)  { percent decimal }
    set_text(PkDwell,'Pk Dwell')
    set_knob_unit(PkDwell,KNOB_UNIT_PERCENT)

    PkDwell := MainParms[uDwell] { Default Instrument Dwell }
    
  { ----------- Vibrato Controls, Column 3 --------------- }
  declare ui_label SpeedCap (1,1)  { Speed Caption }

    set_text(SpeedCap,'  Vibrato Speed')
  declare ui_value_edit SpdDrift (0,MaxSpdDrift,1)
    set_text(SpdDrift,'Speed Drift')

    SpdDrift := MainParms[uSpdDrift]  { Max Speed drift in percent }
  on_init_Speed  { Declare Speed Knob and associated MIDI controls }

    
  { ------------ Vibrato Controls, Column 4 --------------- }
  declare ui_label VarCap (1,1) { Randomization Caption }

    set_text(VarCap,'Random Variation')
  declare ui_value_edit WidthVar (0,MaxVar,1) { Width variation in percent }
    set_text(WidthVar,'Width Var')

    WidthVar := MainParms[uVWidth]   { Default Instrument Width variation }
  declare ui_value_edit DepthVar (0,MaxVar,1) { Depth variation in percent }
    set_text(DepthVar,'Depth Var')

    DepthVar := MainParms[uVDepth]   { Default Instrument Depth variation } 
  declare ui_value_edit SpdPer (0,MaxVar,1)
    set_text(SpdPer,'Speed Per')

    SpdPer := MainParms[uSpdPer]   { Max Speed drift per cycle in percent }
  declare ui_value_edit WavDrift (0,MaxVar,1)  { Max Dwell drift in percent }
    set_text(WavDrift,'Wave Drift')

    WavDrift := MainParms[uWavDrift]
  declare ui_value_edit WavPer (0,MaxVar,1)
    set_text(WavPer,'Wave Per')  

    WavPer := MainParms[uWavPer]   { Max Dwell drift per cycle in percent }
  
  { ------------ Vibrato Controls, Column 5 --------------- }
  declare ui_label EnvCap (1,1)

    set_text(EnvCap,'Envelope Control')
  declare ui_value_edit Onset (1,MaxOnset,1)  { in cycles }
    set_text(Onset,'Onset')

    Onset := MainParms[uOnset] { Default Instrument Onset }

  declare ui_value_edit Rise (1,MaxRise,1)      { in cycles }
    set_text(Rise,'Rise')

    Rise := MainParms[uRise]    { Default Instrument Rise } 
  declare ui_value_edit Decay (1,MaxDecay,1)    { in cycles }
    set_text(Decay,'Decay')

    Decay := MainParms[uDecay]  { Default Instrument Decay }

  declare ui_value_edit Sustain (0,100,1)  { percent }
    set_text(Sustain,'Sustain')

    Sustain := MainParms[uSustain] { Default Instrument Sustain }
  declare ui_menu EnvMenu
    add_menu_item (EnvMenu,'Vib-Amt Only',Amt_Only)
    add_menu_item (EnvMenu,'Envelope Only',Env_Only)
    add_menu_item (EnvMenu,'Vib-Amt + Env',Amt_and_Env)    
    EnvMenu := Amt_and_Env        
  { ----------- Vibrato Controls, Column 6 --------------- }  
  declare ui_label IRCap (1,1)

    set_text(IRCap,'Instrument Range')
  declare @LowKey      { String version of Global[G_Low_Key] }

  declare @TopKey      { String version of Global[G_Top_Key] }  

  declare ui_label IR_Box (1,2)

    ShowIR  { Display default or last saved IR }   
  declare ui_knob VibAmt (0,100,1) { Vibrato Amount Knob }
    set_text(VibAmt,'Vib-Amt')
    set_knob_unit(VibAmt,KNOB_UNIT_PERCENT)
    VibAmt := DefAmt 
  on_init_AmtCC
  
  on_init_CCMenus       { Build the CC assignment menus }
  on_init_Std_PanelMap  { Build the StdPanel_Map, *SVS* }      
  { ------------------- RenPanel & ErrPanel --------------------- }

  on_init_RenPanel     { Declare and initialize Rename-Panel, *UPS* }

  on_init_ErrPanel     { Declare and initialize Error-Panel, *UPS*  } 
  
  { --------------------- Interscript Support Data ------------------- }

  declare markx := -1  { Current note set id mark index }    
  
  { ---------------------- General Variables ---------------------- }

  declare 0db         { Boost needed to get to original level }
  declare 0tune       { Drop in pitch needed to restore initial tuning } 
  declare ActiveID := NoID  { ID of currently-sounding note }
  declare Bending     { Set while legato script using tuning function }
  declare CC_Pending  { 0 = Idle, 1 = waiting for CC selection, }

                      { -1 = Cancellation request by another BtnMenu }

                      { -2 = 'Outside' cancellation request }
  declare Command := No_Cmd    { Preset Menu Active Command }
  declare LastCC      { Last CC moved, used by Assign_CC & Assign_CCA }
  declare MenuActv    { Flag set while a BtnMenu is open }   
  declare New_Note         { Latest note played }
  declare ParentID := NoID { ID of played note that generates child note }
  declare Pitch_Step       { Vibrato tuning needed per tick in mcents }
  declare Vib_Actv    { Vibrato effect still running }
  declare Vib_State   { Current state of the Envelope Generator }

  declare Vol_Step    { Tremolo volume change needed per tick in mdb }
  
  { --------- Save/Restore the following with preset/patch ---------- }
  make_persistent(Preset)      { Column 1}
  make_persistent(Depth.Knob)
  make_persistent(Depth.iKnob)
  make_persistent(Depth.Range)
  make_persistent(Depth.CC)
  
  make_persistent(Width)       { Column 2}
  make_persistent(PkDwell)

  
  make_persistent(Speed.Knob)  { Column 3 }
  make_persistent(Speed.iKnob)
  make_persistent(SpdDrift)
  make_persistent(Speed.Range)
  make_persistent(Speed.CC)
  
  make_persistent(WidthVar)    { Column 4 }
  make_persistent(DepthVar)
  make_persistent(SpdPer)
  make_persistent(WavDrift)
  make_persistent(WavPer)
  
  make_persistent(Onset)       { Column 5 }

  make_persistent(Rise)

  make_persistent(Decay)

  make_persistent(Sustain)
  make_persistent(EnvMenu)

  
  make_persistent(VibAmt)      { Column 6 }
  make_persistent(Amt.CC)
  
  make_persistent(Global)       { Inter-Script Data Array }

  make_persistent(UserParms)    { User Presets } 

  make_persistent(UserNames)    { User Preset Names, numeric }  
  
  _read_persistent_var(UserParms)   { Recover Prior Presets, V110 and up }

  _read_persistent_var(PresetParms) { Recover Prior Presets, V105 }

  Update_Format   { If version upgrade, re-format presets, *UPS* }
  message('')     { Clear K2's status area }

end on { init }


on note

  ISCS_NCB(MsgHandler,OmniHandler)

  if not in_range(EVENT_NOTE,Global[G_Low_Key],Global[G_Top_Key])  

    exit  { Don't process out-of-range notes }

  end if
  if ((EVENT_PAR[3] .and. SID_Mask) = SLS_ID)
    SetActiveID(EVENT_PAR[3]) { SLS Note, activate the SVS }
  else { not an SLS-generated note, disable the SVS }
    ActiveID := NoID
    exit
  end if

  Vib_State := Restart     { Reset the Envelope Generator }

  if Vib_Actv = false  { If Do_Vibrato is not yet running,   }

    Vib_Actv := true   {  start it now and set Vib_Actv flag } 

    Do_Vibrato       { Do_Vibrato will continue to loop until  }

    Vib_Actv := false  {  the current phrase ends. But callbacks }   

  end if             {  can continue because of wait() calls.  }

end on { note }

function SetActiveID(ep3)
  declare new_markx
  
  new_markx := sh_right(ep3,11) .and. Parm_Mask
  if new_markx # markx
    markx := new_markx
    ActiveID := by_marks(Mark[markx])
  else { This note set is already active }
    exit
  end if
end function { SetActiveID }


on release
  ISCS_RCB(CallHandler)
end on { release }



on controller

  if CC_NUM = 0

    exit  { CC0 Never Used by SIPS }

  end if
  LastCC := CC_NUM { Remember for CC Assign function }  

  Update_Panel     { If CC is assigned, update panel controls }

end on { controller }

{ ------------------------ UI Callbacks ---------------------- }
                     { Assign or De-assign MIDI CCs }
on ui_control (Amt.Btn) { User is assigning/de-assigning the Vib Amount CC }

  SetCC(Amt)

end on { Amt.Btn }
                     
on ui_control (Depth.Btn) { Assign Depth CC }

  SetACC(Depth)

end on { Depth.Btn } 

on ui_control (Speed.Btn) { Assign Speed CC }

  SetACC(Speed)

end on { Speed.Btn }

                     { Change CC add-on Range }

on ui_control (Depth.Range)  { User has changed the Depth CC add-on range }
  Change_Range(Depth)

end on { Depth.Range }

on ui_control (EnvMenu)
  Vib_State := Restart
end on { EnvMenu }



on ui_control (Speed.Range)  { User has changed the Speed CC add-on range }
  Change_Range(Speed)  

end on { Speed.Range }

                         { Set Panel Knobs }

on ui_control (Depth.Knob)   { User has changed the Depth Knob value }
  Set_Knob(Depth)

end on { Depth.Knob }



on ui_control (Speed.Knob) { User has changed the Speed Knob value }
  Set_Knob(Speed)

end on { Speed Knob }
                       { Preset Menu }
on ui_control (Preset) { Process Preset Menu selection }

  Do_Preset        { Process Menu selection }

end on { Preset Menu }

import "SIPSXM-Ren V151.txt"  { UI Callbacks for Rename & BtnMenus }


{ ------------------------ ISCS Handlers ---------------------- }
function MsgHandler(sender,msgtype,reply)
  if ((sender = HostSID) and (msgtype = import_block)...
          and (Preset = Import))

    Do_Import  { OK to receive Preset Import }

    reply := mrOK

  end if
end function { MsgHandler }

function OmniHandler(sender,msgtype)
  select sender
    case SLS_ID
      SLS_OmniHandler(msgtype)
  end select
end function { OmniHandler }

function SLS_OmniHandler(msgtype)
  select msgtype
    case starting_xfade
      Bending := true       { Legato script is working on tuning }

      Vib_State := Restart  { Disable Vibrato for now }
    case ending_xfade
      Bending := false      { Re-enable Vibrato }
    case new_inst_range
      Global[G_Low_Key] := EVENT_PAR[0] { Update Inst Range }
      Global[G_Top_Key] := EVENT_PAR[1] 
      ShowIR                { Display new Instrument Range }
    case ending_phrase
      ActiveID := NoID
      Vib_State := Restart
      markx := -1 
  end select
end function { SLS_OmniHandler }

function CallHandler(selector)
  select selector
    case FetchPreset
      Recall_Preset(Preset)
  end select
end function { CallHandler }
{ ------------------------ ISCS Handlers ---------------------- }

{ --------------------- SVS-Specific Routines ----------------------- }

function Do_Cycle(ramp_ticks,flat_ticks)

{ Generates one vibrato cycle of the active note. Since most of 

  the time in this routine is spent 'waiting', note callbacks can

  continue to process new notes.  If an outside process sets

  Vib_State to Restart, Do_Cycle will stop and exit within one Tick }

  Ramp_Up(0,ramp_ticks)  { Segment #1 }

  Stay_Flat(flat_ticks)  { Segment #2 }

  Ramp_Down(ramp_ticks,-ramp_ticks)  { Segments #3 and #4 }

  Stay_Flat(flat_ticks)  { Segment #5 }

  Ramp_Up(-ramp_ticks,0) { Segment #6 }

end function { Do_Cycle }



function Do_Envelope(env,speed_drift,dwell_drift)

{ Generates the vibrato intesity envelope (strength of pitch and

  volume modulation) in steps of one-cycle each. The envelope is

  zero for the initial onset Delay interval and then ramps up to 

  Full_Env. Then, the envelope slowly declines to Min_Env over 

  the Decay interval. Each interval is some integral #of cycles.
  NOTE: This routine is also used to reset speed and dwell drift
  when the Vib_State is Restart }

  declare Decay_Step

  declare Min_Env

  declare Onset_Countdown

  declare Ramp_Step

  

  select Vib_State

    case Restart

      env := 0   { Set the envelope to zero and initialize parms }

      Onset_Countdown := Onset

      Min_Env := Full_Env*Sustain/100

      Decay_Step := (Full_Env - Min_Env)/Decay

      Ramp_Step := Full_Env/Rise

      0db := 0
      dwell_drift := 0
      speed_drift := 0

      Vib_State := Onset_Delay   { Begin Onset_Delay next cycle }

    case Onset_Delay

      dec(Onset_Countdown)       { When Delay cycles are completed, }

      if Onset_Countdown <= 0    {  begin Ramp_To_Full next cycle   }

        Vib_State := Ramp_To_Full

      end if

    case Ramp_To_Full

      env := env + Ramp_Step    { Ramp env from zero to Full_Env   }

      if env >= Full_Env        { in increments of Ramp_Step. Then }

        env := Full_Env         { begin Diminuendo state after the }

        Vib_State := Diminuendo { next cycle.                      }

      end if

    case Diminuendo

      env := env - Decay_Step   { Reduce env from Full_Env to  }

      if env < Min_Env          {  Min_Env in increments of    }

        env := Min_Env          { Decay_Step. Then keep Env at }

      end if                    { Min_Env until Do_Envelope    }

  end select                    { is restarted.                }

end function { Do_Envelope }



function Do_Vibrato

{ Modulates the active note in pitch and volume to simulate Vibrato }

  declare const PollTime := 1000  { 1ms }

  declare DwellDrift

  declare Env

  declare PeakPitch

  declare PeakVol

  declare SpeedDrift

  declare TicksPerCycle

  declare TicksPerFlat

  declare TicksPerRamp

  

  while ActiveID # NoID

    { Continue effect except during SLS crossfades }

    if Bending = false
      Do_Envelope(Env,SpeedDrift,DwellDrift)
      if EnvMenu = Amt_Only
        Env := Full_Env
      end if
      if ((EnvMenu = Amt_and_Env) or (EnvMenu = Amt_Only))
        Env := Env*VibAmt/100 
      end if

      SpeedDrift := SpeedDrift + random(-SpdPer,SpdPer)

      if abs(SpeedDrift) > SpdDrift      { Update accumulated Speed drift }

        SpeedDrift := SpdDrift*abs(SpeedDrift)/SpeedDrift 

      end if

      { Compute the number of clock ticks for each full }

      {  cycle of the waveform at the drifted speed     }

      TicksPerCycle := 1000000*(100 + SpeedDrift)/Speed.Knob/Tick

      DwellDrift := DwellDrift + random(-WavPer,WavPer)

      if abs(DwellDrift) > WavDrift  { Update accumulated Dwell drift }

        DwellDrift := WavDrift*abs(DwellDrift)/DwellDrift 

      end if

      { Compute the #of clocks for the flat top & bottom of the waveform }

      TicksPerFlat := (TicksPerCycle*PkDwell*(100 + DwellDrift) + 10000)/20000

      { Compute the remaining ticks for the 4 ramps of the waveform }

      TicksPerRamp := (TicksPerCycle - 2*TicksPerFlat + 2)/4

      PeakPitch := (100 + random(-WidthVar,WidthVar))*Width/10

      PeakVol := (100 + random(-DepthVar,DepthVar))*Depth.Knob/10

      Pitch_Step := PeakPitch*Env/Full_Env/TicksPerRamp

      Vol_Step := PeakVol*Env/Full_Env/TicksPerRamp

      { Now, generate one vibrato cycle with 6-segment, trapezoidal wave }

      Do_Cycle(TicksPerRamp,TicksPerFlat)

    else { SLS is crossfading }

      wait(PollTime)  { Re-check for end of crossfade, once per ms }

    end if

  end while

end function  { Do_Vibrato }



function Ramp_Down(vx1,vx2)

{ Generates falling ramp of pitch & volume from Pitch_Step*(vx1-1) to

  Pitch_Step*vx2.  Pitch stepping occurs before each 100 usec Tick

  and concurrently with each Pitch_Step is a corresponding Vol_Step }

  declare Atn
  declare tune_amt

  declare vx

  

  vx := vx1

  while ((vx  > vx2) and (Vib_State # Restart))

    dec(vx)
    tune_amt := Pitch_Step*vx

    change_tune(ActiveID,tune_amt - 0tune,1)
    0tune := tune_amt

    Atn := Vol_Step*vx

    change_vol(ActiveID,0db - Atn,1)

    0db := Atn 

    wait(Tick)

  end while

end function { Ramp_Down }



function Ramp_Up(vx1,vx2)

{ Generates rising ramp of pitch & volume from Pitch_Step*(vx1+1) to 

  Pitch_Step*vx2. Pitch stepping occurs before each 100 usec Tick 

  and concurrently with each Pitch_Step is a corresponding Vol_Step }

  declare Atn
  declare tune_amt

  declare vx

  

  vx := vx1

  while ((vx < vx2) and (Vib_State # Restart))

    inc(vx)
    tune_amt := Pitch_Step*vx    

    change_tune(ActiveID,tune_amt - 0tune,1)
    0tune := tune_amt

    Atn := Vol_Step*vx

    change_vol(ActiveID,0db - Atn,1)

    0db := Atn 

    wait(Tick)

  end while

end function { Ramp_Up }



function Stay_Flat(ticks)

{ Hold last pitch and volume for 'ticks' clock cycles }

  declare vx

  

  vx := 0

  while ((vx < ticks) and (Vib_State # Restart))

    inc(vx)

    wait(Tick)

  end while

end function { Stay_Flat }
